文章目录

翻译自Replacing switch statements with Object literals

在许多编程语言中,都有switch语句 - 但是它还应该存在吗?如果你是一个JavaScript程序员,那么你经常会跳出对象,创建,实例化和操作它们。对象是非常灵活的,它们几乎是JavaScript中的一切,使用对象而不是使用switch语句是我最近一直在做的。

switch语句的问题

switch语句有多个问题,从其过程控制流到其处理代码块的方式是非标准的,而JavaScript的其它流程控制语句使用的时花括号。它既不是语法上最好的,也不是设计上最好的。我们不得不在每个case语句后面添加break语句,这可能导致难以调试和产生嵌套的错误。因此,进一步讲,我们应该避免使用case语句。道格拉斯·克罗克福德(Douglas Crockford)曾经多次写过和说过,谨慎对待switch语句。

我们经常在javascript中使用对象查找数据,但我们通常不会考虑使用switch。所以为什么不使用对象字面量来替换switch呢?对象更加灵活,具有更好的可读性和可维护性,且我们不需要手动添加break和case语句;它们对于新的JavaScript开发人员来说也是很友好的,因为它们是标准的数据对象。

随着”case”数量的增加,对象(哈希表)的性能要好于switch的平均成本(case的顺序很重要)。对象方法是哈希表查找,而switch必须评估每个case语句,直到它遇到匹配的case和break。

对象字面量查找

我们一直使用对象,无论是构造函数还是字面量。通常,我们使用它们进行对象查找,从Object属性中获取值。

现在我们设置一个简单的对象字面量,它只返回一个String类型的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
function getDrink (type) {
var drinks = {
'coke': 'Coke',
'pepsi': 'Pepsi',
'lemonade': 'Lemonade',
'default': 'Default item'
};
return 'The drink I chose was ' + (drinks[type] || drinks['default']);
}
var drink = getDrink('coke');
// The drink I chose was Coke
console.log(drink);

在没有default case的情况下我们甚至可以进一步简化

1
2
3
4
5
6
7
function getDrink (type) {
return 'The drink I chose was ' + {
'coke': 'Coke',
'pepsi': 'Pepsi',
'lemonade': 'Lemonade'
}[type];
}

然而,我们可能需要比String更复杂的代码,这可放置在一个函数中来实现。为了简洁起见,我从新创建的函数返回上面的字符串:

1
2
3
4
5
6
7
8
9
10
11
12
13
var type = 'coke';
var drinks = {
'coke': function () {
return 'Coke';
},
'pepsi': function () {
return 'Pepsi';
},
'lemonade': function () {
return 'Lemonade';
}
};

不同的是我们需要调用对象字面量的函数:

1
drinks[type]();

这样更可以更好的维护和有更号的可读性。我们也不用担心break和case语句的落空 - 这只是一个简单的对象。

通常,我们将把一个switch置于一个函数中并获得一个返回值,所以让我们在这里做同样的事情,并将一个对象字面查找转换成一个可用的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function getDrink (type) {
var drinks = {
'coke': function () {
return 'Coke';
},
'pepsi': function () {
return 'Pepsi';
},
'lemonade': function () {
return 'Lemonade';
}
};
return drinks[type]();
}
// let's call it
var drink = getDrink('coke');
console.log(drink); // 'Coke'

这样就很好,也很容易,但这没有”默认值”的情况,然而我们可以轻松创建:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function getDrink (type) {
var fn;
var drinks = {
'coke': function () {
return 'Coke';
},
'pepsi': function () {
return 'Pepsi';
},
'lemonade': function () {
return 'Lemonade';
},
'default': function () {
return 'Default item';
}
};
// if the drinks Object contains the type
// passed in, let's use it
if (drinks[type]) {
fn = drinks[type];
} else {
// otherwise we'll assign the default
// also the same as drinks.default
// it's just a little more consistent using square
// bracket notation everywhere
fn = drinks['default'];
}
return fn();
}
// called with "dr pepper"
var drink = getDrink('dr pepper');
console.log(drink); // 'Default item'

我们可以简化上述if和else语句,在表达式里面使用或||运算符:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function getDrink (type) {
var drinks = {
'coke': function () {
return 'Coke';
},
'pepsi': function () {
return 'Pepsi';
},
'lemonade': function () {
return 'Lemonade';
},
'default': function () {
return 'Default item';
}
};
return (drinks[type] || drinks['default'])();
}

这将在括号()中的两个对象中的表达式中查找。然后调用表达式的结果。如果在查找中找不到drinks[type],它将默认为[‘default’],这样就简单!

我们也不必总是返回内部函数,我们可以改变对变量的引用,然后返回它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function getDrink (type) {
var drink;
var drinks = {
'coke': function () {
drink = 'Coke';
},
'pepsi': function () {
drink = 'Pepsi';
},
'lemonade': function () {
drink = 'Lemonade';
},
'default': function () {
drink = 'Default item';
}
};
// invoke it
(drinks[type] || drinks['default'])();
// return a String with chosen drink
return 'The drink I chose was ' + drink;
}
var drink = getDrink('coke');
// The drink I chose was Coke
console.log(drink);

这些是非常基本的解决方案,对象字面量持有一个返回String的函数,在你只需要一个String的情况下,您可以使用一个String作为键的值-某些时候会从函数返回逻辑。如果使用字符串来混合函数,则可能会更容易地使用函数来保存查找类型并如果它是函数的话就调用它 - 我们可不想去调用一个字符串类型的变量。

对象字面量匹配多个条件

使用switch,我们可以让它们匹配多个条件(这意味着一个以上的情况可以应用于特定的代码):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var type = 'coke';
var snack;
switch(type) {
case 'coke':
case 'pepsi':
snack = 'Drink';
break;
case 'cookies':
case 'crisps':
snack = 'Food';
break;
default:
drink = 'Unknown type!';
}
console.log(snack); // 'Drink'

我们让coke和pepsi通过不添加break来匹配。对象字面量进行此操作也是很简单的,也更具声明性,同时也不容易出错。我们的代码突然变得更加结构化,更有可读性和可重复使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function getSnack (type) {
var snack;
function isDrink () {
return snack = 'Drink';
}
function isFood () {
return snack = 'Food';
}
var snacks = {
'coke': isDrink,
'pepsi': isDrink,
'cookies': isFood,
'crisps': isFood,
};
return snacks[type]();
}
var snack = getSnack('coke');
console.log(snack); // 'Drink'

总结

对象字面量是JavaScript中更自然的流量控制,switch有点老且笨重,容易出现难以调试的错误。Objects更可以更好地扩展,更好地维护,我们也可以对它们进行更好的测试。它们也是设计模式的一部分,并且在其他编程任务中日常使用。对象字面量可以包含函数以及任何其他对象类型,这使得它们变得很灵活!字面量中的每个函数也有函数​​作用域,所以我们可以从我们调用的父函数返回闭包(在这种情况下getDrink返回闭包)。

文章目录